package b1w1nn1ng.crossf1t.business;

import java.sql.SQLException;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.http.HttpServletRequest;

import b1w1nn1ng.crossf1t.db.Database;

/**
 * Contains functions for the login process, including logging in, creating a new account, and recovering passwords.
 * Closely coupled with login.jsp in the view layer.
 * 
 * Version 1.1.0
 * Date Created: May 4, 2011
 * @Author Chris
 */
public class Login {
	/**
	 * Attempts to create a new user in the database with the information contained in the
	 * passed-in user.
	 * @param user The new user account information.
	 * @return True if the account was successfully created, false otherwise.
	 * @throws ClassNotFoundException 
	 * @throws SQLException 
	 */
	public static User createAccount(User user) throws SQLException, ClassNotFoundException{
		if(!(Database.isUsername(user.getUsername()))){
			user = Database.addUser(user);
			//makes sure that the original id is overwritten by the actual id in the db
			return user;
		}
		return null;
	}

	/**
	 * Constructs a dummy user which encapsulates the username, password, security
	 * question and answer, given a username.
	 * 
	 * This method is used in querying the database for information required for
	 * recovering a password.
	 * 
	 * @param username The username of the user who's data should be pulled from the
	 * database
	 * @return A User containing the username, password, security question and answer
	 * of the requested user.
	 * @throws SQLException 
	 * @throws ClassNotFoundException 
	 */
	public static User getPasswordRecoveryInformation(String username) throws ClassNotFoundException, SQLException {
		User ret = Database.getUserByUsername(username);
		/*
		ret.setAnswer(Database.getAnswerByUsername(username));
		ret.setQuestion(Database.getQuestionByUsername(username));
		ret.setPassword(Database.getPasswordByUsername(username));
		ret.setUsername(username);
		 */
		return ret;
	}
	/**
	 * Checks if the password string matches the password of the given user.
	 * @param user The user whose password is being compared.
	 * @param passwordGuess The password being compared.
	 * @return true if the user's password is the same as the guess, false otherwise.
	 * @author Chris Avery
	 */
	public static boolean passwordMatches(User user, String passwordGuess) {
		if(user.getAnswer().equals(passwordGuess))
			return true;
		return false;
	}
	
	/**
	 * Used for semi-sanitizing inputs, returns true if the input string is entirely
	 * composed of letters, numbers, or the characters *$_-!
	 * 
	 * @param input The string to be evaluated
	 * @return true if the string contains only those characters, false otherwise
	 */
	
	public static boolean isAlphaNumeric(String input) {
		Pattern permittedChars = Pattern.compile("[A-Za-z0-9*$_-]+");
		Matcher matcher = permittedChars.matcher(input);
		
		return matcher.matches();
	}
	/**
	 * Used for validating input, sees if a user's input follows the standard format
	 * of something@something.xxx
	 * @param input The string to be evaluated
	 * @return true if the string is a valid email address, false otherwise
	 */
	public static boolean isValidEmail(String input) {
		input = input.toUpperCase();
		Pattern permittedRegex = Pattern.compile("\\b[A-Z0-9._%-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}\\b");
		Matcher matcher = permittedRegex.matcher(input);
		
		return matcher.matches();
	}
	
	/**
	 * Checks if the input is an integer.
	 * 
	 * @param input The value to be checked.
	 * @return true if the string is a valid integer, false otherwise
	 */
	public static boolean isPositiveInteger(String input) {
		try {
			int i = Integer.parseInt(input);
			if(i > 0)
				return true;
			return false;
		} 
		catch(NumberFormatException e) {
			return false;			
		}
	}
	
	public static String validate(HttpServletRequest request) throws SQLException, ClassNotFoundException {
		//Load the inputs into variables for ease of use
		String username = "", password1 = "", password2 = "", 
		email1 = "", email2 = "", firstName = "", lastName = "", 
		question = "", answer = "", weightString = "", 
		heightString = "";
		if(request.getParameter("username") != null)
			username = request.getParameter("username");
		if(request.getParameter("password1") != null)
			password1 = request.getParameter("password1");
		if(request.getParameter("password2") != null)
			password2 = request.getParameter("password2");
		if(request.getParameter("email1") != null)
			email1 = request.getParameter("email1");
		if(request.getParameter("email2") != null)
			email2 = request.getParameter("email2");
		if(request.getParameter("firstName") != null)
			firstName = request.getParameter("firstName");
		if(request.getParameter("lastName") != null)
			lastName = request.getParameter("lastName");
		if(request.getParameter("question") != null)
			question = request.getParameter("question");
		if(request.getParameter("answer") != null)
			answer = request.getParameter("answer");
		if(request.getParameter("weight") != null)
			weightString = request.getParameter("weight");
		if(request.getParameter("height") != null)
			heightString = request.getParameter("height");
		
		StringBuilder errorString = new StringBuilder();
		
		//Validate all the inputs 
		//Validate username
		if(username.trim().equals("")) {
			errorString.append("<li>Please enter a username</li>");
		} else if(!Login.isAlphaNumeric(username)) {
			errorString.append("<li>The username you entered is invalid. Usernames may only contain letters, numbers, and the symbols -!*$_</li>");
		} else if(Database.isUsername(username)) {
			errorString.append("<li>That username is already in use. Please try another.</li>");
		}
		//Validate password
		if(password1.trim().equals("")) {
			errorString.append("<li>Please enter a password</li>");
		} else {
			if(!Login.isAlphaNumeric(password1)) {
				errorString.append("<li>The password you entered is invalid: Passwords may only contain letters, numbers, and the symbols -!*$_</li>");
			}
			if(password1.length() < 8) {
				errorString.append("<li>Your password must be at least 8 characters long</li>");
			}
			if(password2.trim().equals("")) {
				errorString.append("<li>Please confirm your password</li>");
			} else if(!password1.equals(password2)) {
				errorString.append("<li>Your password confirmation does not match your password</li>");
			}
		}
		//Validate email
		if(email1.trim().equals("")) {
			errorString.append("<li>Please enter an email</li>");
		} else {
			if(!Login.isValidEmail(email1)) {
				errorString.append("<li>The email you entered is invalid: Please enter a valid email</li>");
			}
			if(email2.trim().equals("")) {
				errorString.append("<li>Please confirm your email</li>");
			} else if(!email1.equals(email2)) {
				errorString.append("<li>Your email confirmation does not match your email</li>");
			}
		}
		//Validate security question/answer
		if(question.equals("")) {
			errorString.append("<li>Please select a security question</li>");
		} else if(answer.trim().equals("")) {
			errorString.append("<li>Please enter an answer to the security question</li>");
		}
		//Validate first name
		if(firstName.equals("")) {
			errorString.append("<li>Please enter your first name</li>");
		}
		//Validate last name
		if(lastName.equals("")) {
			errorString.append("<li>Please enter your last name</li>");
		}
		//Validate weight
		if(weightString.trim().equals("")) {
			errorString.append("<li>Please enter your weight: We won't judge</li>");
		} else if(!Login.isPositiveInteger(weightString)) {
			errorString.append("<li>The weight you entered is invalid: Please enter a postitive integer</li>");
		}
		//Validate height
		if(heightString.trim().equals("")) {
			errorString.append("<li>Please enter your height</li>");
		} else if(!Login.isPositiveInteger(heightString)) {
			errorString.append("<li>The height you entered is invalid: Please enter a postitive integer</li>");
		}
	return errorString.toString();
	}
	
	/**
	 * Gets a user from the database by their username. Pretty worthless class, just exists to enforce layering.
	 * 
	 * @param username The username of the user we're pulling
	 * @return A user object containing that user's data, or null if that user doesn't exist.
	 * @throws SQLException
	 * @throws ClassNotFoundException
	 * @author Chris
	 */
	
	public static User getUserByUsername(String username) throws SQLException, ClassNotFoundException {
		//Test stub return
		/*if(!username.equals("name"))
			return null;
		User test = new User();
		test.setFirstName("firstName");
		test.setUsername("name");
		test.setQuestion("Question: (Answer=foo)");
		test.setAnswer("foo");
		test.setPassword("password");
		test.setEmail("cse-110@googlegroups.com");
		return test;*/
		
		return Database.getUserByUsername(username);
	}
	
	public static String generateRandomPassword(int length) {
		final String charset = "!0123456789abcdefghijklmnopqrstuvwxyz";
		
		Random rand = new Random(System.currentTimeMillis());
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < length; i++) {
            int pos = rand.nextInt(charset.length());
            sb.append(charset.charAt(pos));
        }
        return sb.toString();
	}
}

